#ifndef __wWINDOW_H__
#define __wWINDOW_H__

#if !WINDOWS_SYSTEM
	#error only windows system supported
#endif

#ifndef __ETL_H__
#error wwindow.h requires etl.h to be included first
#endif

#ifndef __ETLBASE_H__
#error wwindow.h requires etlbase.h to be included first
#endif

#ifndef __wUIKIT_H__
#error wwindow.h requires wuikit.h to be included first
#endif

#include <shellapi.h>
#pragma comment(lib, "shell32.lib")

inline HWND wCreateMsgWindow(
	LPCTSTR lpClassName,  // registered class name
	LPCTSTR lpWindowName, // window name
	DWORD dwStyle,        // window style
	DWORD dwExStyle = 0,
	HWND hWndParent = NULL,      // handle to parent or owner window
	HINSTANCE hInstance = NULL,  // handle to application instance
	int x = 0,                // horizontal position of window
	int y = 0,                // vertical position of window
	int nWidth = 200,           // window width
	int nHeight = 150          // window height
)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX); 
	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_NOCLOSE;
	wcex.lpfnWndProc	= (WNDPROC)DefWindowProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= lpClassName;
	wcex.hIconSm		= NULL;
	RegisterClassEx(&wcex);
	return CreateWindowEx(dwExStyle, lpClassName, lpWindowName
		, dwStyle, x, y, nWidth, nHeight
		, hWndParent, NULL, hInstance, NULL);
}

//////////////////////////////////////////////////////////////////////////
// wWindowLock class
class wWindowLock
{
	HCURSOR m_holdCursor;
public:
	wWindowLock(HWND hWnd = NULL)
	{
		if(hWnd == NULL)
			hWnd = ::GetDesktopWindow();
		m_holdCursor = ::SetCursor(LoadCursor(NULL, IDC_WAIT));
		::LockWindowUpdate(hWnd);
	}
	~wWindowLock()
	{
		::LockWindowUpdate(NULL);
		::SetCursor(m_holdCursor);
	}
};

template<class TPre>
class wTrayIcon : public NOTIFYICONDATA
{
protected:
	virtual BOOL OnTrayIconNotify(UINT uMouseEvent, POINT ptMouse) = 0;
	BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)
	{
		if(NOTIFYICONDATA::hWnd != NULL && uMsg == NOTIFYICONDATA::uCallbackMessage)
		{
			POINT pt = {0,0};
			GetCursorPos(&pt);
			return OnTrayIconNotify(lParam, pt);
		}
		return FALSE;
	}
public:
	BOOL _InitializeTrayIcon()
	{
		TPre* pThis = (TPre*)this;
		chASSERT(IsWindow(pThis->m_hWnd));

		NOTIFYICONDATA::hIcon = (HICON)pThis->SendMessage(WM_GETICON, TRUE, 0);
		pThis->SendMessage(WM_GETTEXT, chLENOF(NOTIFYICONDATA::szTip), (LPARAM)NOTIFYICONDATA::szTip);
//		chStrcpy<TCHAR>(NOTIFYICONDATA::szInfoTitle, NOTIFYICONDATA::szTip);

		NOTIFYICONDATA::hWnd   = pThis->m_hWnd;
		NOTIFYICONDATA::uCallbackMessage = ::RegisterWindowMessage(_T("msg_LTLTrayIcon"));; 
		
		NOTIFYICONDATA::uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
		return Shell_NotifyIcon(NIM_ADD, (NOTIFYICONDATA*)this); 
	}
	
	void _UninitializeTrayIcon()
	{
		Shell_NotifyIcon(NIM_DELETE, (NOTIFYICONDATA*)this); 
	}
	
	BOOL SetTrayTipText(LPCTSTR strTipText)
	{
		chASSERT(IsWindow(NOTIFYICONDATA::hWnd));
		if(chStrlen<TCHAR>(strTipText) >= chLENOF(NOTIFYICONDATA::szTip))
			return FALSE;
		chStrcpy<TCHAR>(NOTIFYICONDATA::szTip, strTipText);
		NOTIFYICONDATA::uFlags = NIF_TIP;
		return Shell_NotifyIcon(NIM_MODIFY, (NOTIFYICONDATA*)this);
	}
	
	BOOL SetTrayIcon(HICON hIcon)
	{
		chASSERT(IsWindow(NOTIFYICONDATA::hWnd) && hIcon != NULL);
		NOTIFYICONDATA::hIcon = hIcon;
		NOTIFYICONDATA::uFlags = NIF_ICON;
		return Shell_NotifyIcon(NIM_MODIFY, (NOTIFYICONDATA*)this);
	}
	
	BOOL PopInfoText(LPCTSTR strInfoText, UINT uStyle = NIIF_INFO, UINT uDelayTime = 5000)
	{
		chASSERT(IsWindow(NOTIFYICONDATA::hWnd));
		if(chStrlen<TCHAR>(strInfoText) >= chLENOF(NOTIFYICONDATA::szInfo))
			return FALSE;

		chStrcpy<TCHAR>(NOTIFYICONDATA::szInfo, "");
		NOTIFYICONDATA::uTimeout = uDelayTime;
		NOTIFYICONDATA::uFlags = NIF_INFO;
		NOTIFYICONDATA::dwInfoFlags = uStyle;
		Shell_NotifyIcon(NIM_MODIFY, (NOTIFYICONDATA*)this);

		chStrcpy<TCHAR>(NOTIFYICONDATA::szInfo, strInfoText);
		NOTIFYICONDATA::uTimeout = uDelayTime;
		NOTIFYICONDATA::uFlags = NIF_INFO;
		NOTIFYICONDATA::dwInfoFlags = uStyle;
 
		return Shell_NotifyIcon(NIM_MODIFY, (NOTIFYICONDATA*)this);
	}
public:
	wTrayIcon()
	{
		NOTIFYICONDATA tNT = {sizeof(NOTIFYICONDATA)};
		*(NOTIFYICONDATA*)this = tNT;
	}
};

class wWndHelper
{
public:
	HWND FindNextWindow(HWND hWndFrom, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, BOOL bSearchInChild)
	{
		m_lpszClassName = lpszClassName;
		m_lpszWindowName = lpszWindowName;
		
		if(hWndFrom == NULL)
			hWndFrom = ::GetWindow(GetDesktopWindow(), GW_CHILD);
		else
			hWndFrom = ::GetNextWindow(hWndFrom, GW_HWNDNEXT);
		
		while(hWndFrom != NULL)
		{
			if(IsWindowMatched(hWndFrom))
				return hWndFrom;
			
			if(bSearchInChild)
			{
				HWND hTarget = FindInChildWindows(hWndFrom, bSearchInChild);
				if(hTarget != NULL)
					return hTarget;
			}
			
			hWndFrom = ::GetNextWindow(hWndFrom, GW_HWNDNEXT);
		}
		return hWndFrom;
	}

	HWND FindInTopWnd(HWND hTopWnd, HWND hSearchAfter, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, BOOL bSearchInChild)
	{
		chASSERT(::IsWindow(hTopWnd));
		
		m_lpszClassName = lpszClassName;
		m_lpszWindowName = lpszWindowName;
		
		if(hSearchAfter == NULL)
			return FindInChildWindows(hTopWnd, bSearchInChild);

		HWND hTarget = NULL;
		if(bSearchInChild)
		{
			hTarget = FindInChildWindows(hSearchAfter, TRUE);
			if(hTarget != NULL)
				return hTarget;
		}

		hTarget = FindInBrotherWindows(hSearchAfter, bSearchInChild);
		if(hTarget != NULL)
			return hTarget;
		
		return FindInParentWindows(hTopWnd, hSearchAfter, bSearchInChild);
	}
protected:
	HWND FindInChildWindows(HWND hParent, BOOL bSearchInChild)
	{
		chASSERT(::IsWindow(hParent));
		
		HWND hFrom = ::GetWindow(hParent, GW_CHILD);
		while(hFrom != NULL)
		{
			if(IsWindowMatched(hFrom))
				return hFrom;
			
			if(bSearchInChild)
			{
				HWND hTarget = FindInChildWindows(hFrom, TRUE);
				if(hTarget != NULL)
					return hTarget;
			}

			hFrom = ::GetNextWindow(hFrom, GW_HWNDNEXT);
		}
		return NULL;
	}

	HWND FindInBrotherWindows(HWND hSearchAfter, BOOL bSearchInChild)
	{
		chASSERT(::IsWindow(hSearchAfter));
		HWND hFrom = ::GetNextWindow(hSearchAfter, GW_HWNDNEXT);
		
		while(hFrom != NULL)
		{
			if(IsWindowMatched(hFrom))
				return hFrom;
			if(bSearchInChild)
			{
				HWND hTarget = FindInChildWindows(hFrom, TRUE);
				if(hTarget != NULL)
					return hTarget;
			}

			hFrom = ::GetNextWindow(hFrom, GW_HWNDNEXT);
		}
		
		return NULL;
	}
	
	HWND FindInParentWindows(HWND hTopParent, HWND hSearchAfter, BOOL bSearchChild)
	{
		chASSERT(::IsWindow(hTopParent) && ::IsWindow(hSearchAfter));
//		HWND hParent = ::GetWindow(hSearchAfter, GW_OWNER);
		HWND hParent = ::GetParent(hSearchAfter);
		if(hParent == hTopParent )
			return NULL;
		
		HWND hTarget = FindInBrotherWindows(hParent, bSearchChild);
		if(hTarget != NULL)
			return hTarget;

		return FindInParentWindows(hTopParent, hParent, bSearchChild);
	}
private:
	virtual BOOL IsWindowMatched(HWND hThis)
	{
		chASSERT(::IsWindow(hThis));
		
		if(m_lpszWindowName != NULL && m_lpszWindowName[0] != '\0')
		{
			int nLength = ::GetWindowTextLength(hThis) + 1;
			LPTSTR szText = m_strTextBuffer.GetBufferSetLength(nLength);
			::GetWindowText(hThis, szText, nLength);
			if(chFind<TCHAR, false>(szText, m_lpszWindowName) == NULL)
				return FALSE;
		}
		if(m_lpszClassName != NULL && m_lpszClassName[0] != '\0')
		{
			int nLength = MAX_PATH;
			LPTSTR szText = m_strTextBuffer.GetBufferSetLength(nLength);
			::GetClassName(hThis, szText, nLength);
			if(chFind<TCHAR, false>(szText, m_lpszClassName) == NULL)
				return FALSE;
		}
		return TRUE;
	}
public:
	wWndHelper()
	{
		m_lpszClassName = NULL;
		m_lpszWindowName = NULL; 
	}
private:
	chString m_strTextBuffer;
public:
	LPCTSTR m_lpszClassName;
	LPCTSTR m_lpszWindowName; 
};


//class wWndTrackTip : public CWindowImpl<wWndTrackTip>
//{
//	enum {TIMER_DELAY_POP = 0x1001};
//	POINT m_TraceTo;
//		
//	BEGIN_MSG_MAP(LCDeviceComWnd)
//		MESSAGE_HANDLER(WM_TIMER, OnTimer)
//		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnPopTip)
//	END_MSG_MAP()
//public:
//	HWND CreateTraceTip(LPCTSTR sTitle)
//	{
//		HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, TEXT("")
//			, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON | TTS_ALWAYSTIP,
//			CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
//			NULL, (HMENU)NULL, _Module.GetModuleInstance(), NULL);
//		chASSERT(hwndTT != NULL);
//		SubclassWindow(hwndTT);
//
//		TOOLINFO tipInfo = { sizeof(tipInfo) };
//		tipInfo.uFlags = TTF_TRACK;
//
//		chVERIFY(SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&tipInfo));
//		if (sTitle != NULL)
//		{
//			chVERIFY(SendMessage(hwndTT, TTM_SETTITLE, 1, (LPARAM)sTitle));
//		}
//
//		HWND hShellTray = ::FindWindow(_T("Shell_TrayWnd"), NULL);
//		chASSERT(hShellTray != NULL);
//		HWND hTrayNotify = ::FindWindowEx(hShellTray, NULL, _T("TrayNotifyWnd"), NULL);
//		chASSERT(hTrayNotify != NULL);
//		
//		RECT tRect;
//		::GetWindowRect(hTrayNotify, &tRect);
//		m_TraceTo.x = tRect.right - 10;
//		m_TraceTo.y = (tRect.bottom + tRect.top) / 2;
//		return hwndTT;
//	}
//	BOOL DestroyTraceTip()
//	{
//		HWND hwndTT = UnsubclassWindow(TRUE);
//		return ::DestroyWindow(hwndTT);
//	}
//	
//	LRESULT OnPopTip(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//	{
//		TOOLINFO tipInfo = { sizeof(tipInfo) };
//		SendMessage(TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&tipInfo);
//		return 0;
//	}
//	
//	LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
//	{
//		bHandled = FALSE;
//		if(wParam == TIMER_DELAY_POP)
//		{
//			KillTimer(TIMER_DELAY_POP);
//			OnPopTip(uMsg, wParam, lParam, bHandled);
//			bHandled = TRUE;
//		}
//		return 0;
//	}
//	void PopTips(LPCTSTR lpszTraceText, int x = -1, int y = -1)
//	{
//		if (x == -1 || y == -1)
//		{
//			x = m_TraceTo.x;
//			y = m_TraceTo.y;
//		}
//
//		TOOLINFO tipInfo = { sizeof(tipInfo) };
//		tipInfo.lpszText = (LPTSTR)lpszTraceText;
//		SendMessage(TTM_UPDATETIPTEXT, 0, (LPARAM)&tipInfo);
//
//		SendMessage(TTM_TRACKPOSITION, 0, (LPARAM)MAKELPARAM(x, y));
//		SendMessage(TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&tipInfo);
//		
//		SetTimer(TIMER_DELAY_POP, SendMessage(TTM_GETDELAYTIME, TTDT_AUTOPOP, 0));
//	}
//};

#endif //__wWINDOW_H__